/**
 * 表达式取反
 */

import { Expression, BoolOperation, Operator, BoolItem, EMPTY, FULL } from './types';
import { isBoolExpression } from './util';
// import {print} from './print';
import { simplify } from './simplify';

/**
 * 集合取反
 */
export function reverse(oriExp: Expression): Expression {
  const exp = simplify(oriExp);

  if (exp.operator === Operator.FULL) {
    return EMPTY;
  } else if (exp.operator === Operator.EMPTY) {
    return FULL;
  }

  const { AND, OR } = BoolOperation;
  if (isBoolExpression(exp)) {
    const newOp = exp.operator === OR ? AND : OR;
    return {
      operator: newOp,
      args: exp.args.map(reverse).filter((i) => i !== null) as Expression[],
    };
  } else {
    const { operator: oriOp } = exp;
    const { EQ, NEQ, IN, NOT_IN, LT, GT, NGT, NLT } = Operator;
    const reverseMap: { [key in Operator]?: Operator } = {
      [EQ]: NEQ,
      [NEQ]: EQ,
      [IN]: NOT_IN,
      [NOT_IN]: IN,
      [LT]: NLT,
      [GT]: NGT,
      [NGT]: GT,
      [NLT]: LT,
    };

    if (oriOp in reverseMap) {
      return {
        key: exp.key,
        operator: reverseMap[oriOp] as Operator,
        value: exp.value,
      };
    }

    const { RIN, LO_IN_INNER, RO_IN_INNER, O_IN_INNER, SEGMENTS_INNER } = Operator;
    if ([RIN, LO_IN_INNER, RO_IN_INNER, O_IN_INNER].includes(oriOp)) {
      const [left, right] = exp.value as number[];
      const leftOperator = [LO_IN_INNER, O_IN_INNER].includes(oriOp) ? NGT : LT;
      const rightOperator = [RO_IN_INNER, O_IN_INNER].includes(oriOp) ? NLT : GT;

      return {
        key: exp.key,
        operator: SEGMENTS_INNER,
        value: [
          {
            key: exp.key,
            operator: leftOperator,
            value: left,
          },
          {
            key: exp.key,
            operator: rightOperator,
            value: right,
          },
        ],
      };
    } else {
      // SEGMENTS_INNER
      const reversedSeg = (exp.value as BoolItem[])
        .map(reverse)
        .filter((i) => i !== null) as Expression[];
      const unionExp = {
        operator: AND,
        args: reversedSeg,
      };
      return simplify(unionExp);
    }
  }
}
